package com.youlai.boot.system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.youlai.boot.common.exception.BusinessException;
import com.youlai.boot.core.security.util.SecurityUtils;
import com.youlai.boot.shared.websocket.service.OnlineUserService;
import com.youlai.boot.system.converter.NoticeConverter;
import com.youlai.boot.system.enums.NoticePublishStatusEnum;
import com.youlai.boot.system.enums.NoticeTargetEnum;
import com.youlai.boot.system.mapper.NoticeMapper;
import com.youlai.boot.system.model.bo.NoticeBO;
import com.youlai.boot.system.model.dto.NoticeDTO;
import com.youlai.boot.system.model.entity.SysNoticeEntity;
import com.youlai.boot.system.model.entity.SysUserEntity;
import com.youlai.boot.system.model.entity.SysUserNoticeEntity;
import com.youlai.boot.system.model.form.NoticeForm;
import com.youlai.boot.system.model.query.NoticePageQuery;
import com.youlai.boot.system.model.vo.NoticeDetailVO;
import com.youlai.boot.system.model.vo.NoticePageVO;
import com.youlai.boot.system.model.vo.UserNoticePageVO;
import com.youlai.boot.system.service.NoticeService;
import com.youlai.boot.system.service.UserNoticeService;
import com.youlai.boot.system.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 通知公告服务实现类
 *
 * @author Theo
 * @since 2024-08-27 10:31
 */
@Service
@RequiredArgsConstructor
public class NoticeServiceImpl extends ServiceImpl<NoticeMapper, SysNoticeEntity> implements NoticeService {

    private final NoticeConverter noticeConverter;

    private final UserNoticeService userNoticeService;

    private final UserService userService;

    private final SimpMessagingTemplate messagingTemplate;
    private final OnlineUserService onlineUserService;

    /**
     * 获取通知公告分页列表
     *
     * @param queryParams 查询参数
     * @return {@link Page< NoticePageVO >} 通知公告分页列表
     */
    @Override
    public Page<NoticePageVO> getNoticePage(NoticePageQuery queryParams) {
        QueryWrapper query = QueryWrapper.create().from(SysNoticeEntity.class);

        query.leftJoin(SysUserEntity.class)
                .on(q-> q.and(SysUserEntity::getId).eq(SysNoticeEntity::getPublisherId))
                .select(
                        QueryMethods.column(SysUserEntity::getUsername).as(NoticeBO::getPublisherName),
                        QueryMethods.column(SysNoticeEntity::getId),
                        QueryMethods.column(SysNoticeEntity::getTitle),
                        QueryMethods.column(SysNoticeEntity::getType),
                        QueryMethods.column(SysNoticeEntity::getLevel),
                        QueryMethods.column(SysNoticeEntity::getTargetType),
                        QueryMethods.column(SysNoticeEntity::getPublishStatus),
                        QueryMethods.column(SysNoticeEntity::getPublishTime),
                        QueryMethods.column(SysNoticeEntity::getRevokeTime),
                        QueryMethods.column(SysNoticeEntity::getCreateTime),
                        QueryMethods.column(SysNoticeEntity::getUpdateTime)
                        )
                .and(SysNoticeEntity::getTitle).like(queryParams.getTitle(),StrUtil.isNotBlank(queryParams.getTitle()))
                .and(SysNoticeEntity::getPublishStatus).eq(queryParams.getPublishStatus(),queryParams.getPublishStatus() != null);

        if (queryParams.getPublishTime() != null) {
            String startDate = queryParams.getPublishTime().get(0);
            String endDate = queryParams.getPublishTime().get(1);

            if (StrUtil.isNotBlank(startDate) && StrUtil.isNotBlank(endDate)) {
                query.and(SysNoticeEntity::getPublishTime).between(startDate,endDate);
            }

            if (StrUtil.isNotBlank(startDate)) {
                query.and(SysNoticeEntity::getPublishTime).ge(startDate);
            }

            if (StrUtil.isNotBlank(endDate)) {
                query.and(SysNoticeEntity::getPublishTime).le(endDate);
            }
        }

        Page<NoticeBO> noticePage = this.pageAs(
                new Page<>(queryParams.getPageNum(), queryParams.getPageSize()),
                query.orderBy(SysNoticeEntity::getPublishTime).desc().orderBy(SysNoticeEntity::getCreateTime).desc(),
                NoticeBO.class
        );

        return noticeConverter.toPageVo(noticePage);
    }

    /**
     * 获取通知公告表单数据
     *
     * @param id 通知公告ID
     * @return {@link NoticeForm} 通知公告表单对象
     */
    @Override
    public NoticeForm getNoticeFormData(Long id) {
        SysNoticeEntity entity = this.getById(id);
        return noticeConverter.toForm(entity);
    }

    /**
     * 新增通知公告
     *
     * @param formData 通知公告表单对象
     * @return {@link Boolean} 是否新增成功
     */
    @Override
    public boolean saveNotice(NoticeForm formData) {

        if (NoticeTargetEnum.SPECIFIED.getValue().equals(formData.getTargetType())) {
            List<String> targetUserIdList = formData.getTargetUserIds();
            if (CollectionUtil.isEmpty(targetUserIdList)) {
                throw new BusinessException("推送指定用户不能为空");
            }
        }
        SysNoticeEntity entity = noticeConverter.toEntity(formData);
        return this.save(entity);
    }

    /**
     * 更新通知公告
     *
     * @param id       通知公告ID
     * @param formData 通知公告表单对象
     * @return {@link Boolean} 是否更新成功
     */
    @Override
    public boolean updateNotice(Long id, NoticeForm formData) {
        if (NoticeTargetEnum.SPECIFIED.getValue().equals(formData.getTargetType())) {
            List<String> targetUserIdList = formData.getTargetUserIds();
            if (CollectionUtil.isEmpty(targetUserIdList)) {
                throw new BusinessException("推送指定用户不能为空");
            }
        }

        SysNoticeEntity entity = noticeConverter.toEntity(formData);
        return this.updateById(entity);
    }

    /**
     * 删除通知公告
     *
     * @param ids 通知公告ID，多个以英文逗号(,)分割
     * @return {@link Boolean} 是否删除成功
     */
    @Override
    @Transactional
    public boolean deleteNotices(String ids) {
        if (StrUtil.isBlank(ids)) {
            throw new BusinessException("删除的通知公告数据为空");
        }

        // 逻辑删除
        List<Long> idList = Arrays.stream(ids.split(","))
                .map(Long::parseLong)
                .toList();
        boolean isRemoved = this.removeByIds(idList);
        if (isRemoved) {
            // 删除通知公告的同时，需要删除通知公告对应的用户通知状态
            userNoticeService.remove(QueryWrapper.create().in(SysUserNoticeEntity::getNoticeId, idList));
        }
        return isRemoved;
    }

    /**
     * 发布通知公告
     *
     * @param id 通知公告ID
     * @return 是否发布成功
     */
    @Override
    @Transactional
    public boolean publishNotice(Long id) {
        SysNoticeEntity notice = this.getById(id);
        if (notice == null) {
            throw new BusinessException("通知公告不存在");
        }

        if (NoticePublishStatusEnum.PUBLISHED.getValue().equals(notice.getPublishStatus())) {
            throw new BusinessException("通知公告已发布");
        }

        Integer targetType = notice.getTargetType();
        String targetUserIds = notice.getTargetUserIds();
        if (NoticeTargetEnum.SPECIFIED.getValue().equals(targetType)
                && StrUtil.isBlank(targetUserIds)) {
            throw new BusinessException("推送指定用户不能为空");
        }

        notice.setPublishStatus(NoticePublishStatusEnum.PUBLISHED.getValue());
        notice.setPublisherId(SecurityUtils.getUserId());
        notice.setPublishTime(LocalDateTime.now());
        boolean publishResult = this.updateById(notice);

        if (publishResult) {
            // 发布通知公告的同时，删除该通告之前的用户通知数据，因为可能是重新发布
            userNoticeService.remove(
                    QueryWrapper.create().eq(SysUserNoticeEntity::getNoticeId, id)
            );

            // 添加新的用户通知数据
            List<String> targetUserIdList = null;
            if (NoticeTargetEnum.SPECIFIED.getValue().equals(targetType)) {
                targetUserIdList = Arrays.asList(targetUserIds.split(","));
            }

            List<SysUserEntity> targetUserList = userService.list(
                    QueryWrapper.create()
                            // 如果是指定用户，则筛选出指定用户
                            .in(SysUserEntity::getId, targetUserIdList, NoticeTargetEnum.SPECIFIED.getValue().equals(targetType))
            );

            List<SysUserNoticeEntity> userNoticeList = targetUserList.stream().map(user -> {
                SysUserNoticeEntity userNotice = new SysUserNoticeEntity();
                userNotice.setNoticeId(id);
                userNotice.setUserId(user.getId());
                userNotice.setIsRead(0);
                return userNotice;
            }).toList();

            if (CollectionUtil.isNotEmpty(userNoticeList)) {
                userNoticeService.saveBatch(userNoticeList);
            }

            Set<String> receivers = targetUserList.stream().map(SysUserEntity::getUsername).collect(Collectors.toSet());

            Set<String> allOnlineUsers = onlineUserService.getAllOnlineUsers();

            // 找出在线用户的通知接收者
            Set<String> onlineReceivers = new HashSet<>(CollectionUtil.intersection(receivers, allOnlineUsers));

            NoticeDTO noticeDTO = new NoticeDTO();
            noticeDTO.setId(id);
            noticeDTO.setTitle(notice.getTitle());
            noticeDTO.setType(notice.getType());
            noticeDTO.setPublishTime(notice.getPublishTime());

            onlineReceivers.forEach(receiver -> messagingTemplate.convertAndSendToUser(receiver, "/queue/message", noticeDTO));
        }
        return publishResult;
    }

    /**
     * 撤回通知公告
     *
     * @param id 通知公告ID
     * @return 是否撤回成功
     */
    @Override
    @Transactional
    public boolean revokeNotice(Long id) {
        SysNoticeEntity notice = this.getById(id);
        if (notice == null) {
            throw new BusinessException("通知公告不存在");
        }

        if (!NoticePublishStatusEnum.PUBLISHED.getValue().equals(notice.getPublishStatus())) {
            throw new BusinessException("通知公告未发布或已撤回");
        }

        notice.setPublishStatus(NoticePublishStatusEnum.REVOKED.getValue());
        notice.setRevokeTime(LocalDateTime.now());
        boolean revokeResult = this.updateById(notice);

        if (revokeResult) {
            // 撤回通知公告的同时，需要删除通知公告对应的用户通知状态
            userNoticeService.remove(
                    QueryWrapper.create().from(SysUserNoticeEntity.class)
                            .eq(SysUserNoticeEntity::getNoticeId, id)
            );
        }
        return revokeResult;
    }

    /**
     * 阅读获取通知公告详情
     *
     * @param id 通知公告ID
     * @return
     */
    @Override
    public NoticeDetailVO getNoticeDetail(Long id) {
        NoticeBO noticeBO = this.getOneAs(
                QueryWrapper.create().from(SysNoticeEntity.class)
                        .select(
                              QueryMethods.column(SysNoticeEntity::getId),
                              QueryMethods.column(SysNoticeEntity::getTitle),
                              QueryMethods.column(SysNoticeEntity::getContent),
                              QueryMethods.column(SysNoticeEntity::getType),
                              QueryMethods.column(SysNoticeEntity::getLevel),
                              QueryMethods.column(SysNoticeEntity::getPublishStatus),
                              QueryMethods.column(SysNoticeEntity::getPublishTime),
                              QueryMethods.column(SysUserEntity::getNickname).as(NoticeBO::getPublisherName)
                        )
                        .leftJoin(SysUserEntity.class).on(q->q.and(SysUserEntity::getId).eq(SysNoticeEntity::getPublisherId))
                        .and(SysNoticeEntity::getId).eq(id),
                NoticeBO.class
        );
        // 更新用户通知公告的阅读状态
        Long userId = SecurityUtils.getUserId();
        userNoticeService.update(
                new SysUserNoticeEntity().setIsRead(1),
                QueryWrapper.create().from(SysUserNoticeEntity.class)
                        .eq(SysUserNoticeEntity::getNoticeId, id)
                        .eq(SysUserNoticeEntity::getUserId, userId)
                        .eq(SysUserNoticeEntity::getIsRead, 0)
        );
        return noticeConverter.toDetailVO(noticeBO);
    }

    /**
     * 获取当前登录用户的通知公告列表
     *
     * @param queryParams 查询参数
     * @return 通知公告分页列表
     */
    @Override
    public Page<UserNoticePageVO> getMyNoticePage(NoticePageQuery queryParams) {
        queryParams.setUserId(SecurityUtils.getUserId());

        QueryWrapper query = QueryWrapper.create()
                .from(SysUserNoticeEntity.class)
                .eq(SysUserNoticeEntity::getUserId, queryParams.getUserId())
                .innerJoin(SysNoticeEntity.class).on(t1 -> {
                    t1.and(SysUserNoticeEntity::getNoticeId).eq(SysNoticeEntity::getId);
                })
                .leftJoin(SysUserEntity.class).on(t2 -> {
                    t2.and(SysNoticeEntity::getPublisherId).eq(SysUserEntity::getId);
                })
                .and(SysNoticeEntity::getTitle).eq(queryParams.getTitle(), StrUtil.isNotBlank(queryParams.getTitle()));


        query.orderBy(SysUserNoticeEntity::getCreateTime).desc();

        return userNoticeService.pageAs(
                new Page<>(queryParams.getPageNum(), queryParams.getPageSize()),
                query,
                UserNoticePageVO.class
        );
    }

}
